package pl.edu.agh.io.sylvia.logic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.net.nntp.NNTPClient;
import org.apache.commons.net.nntp.NewsgroupInfo;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;

import pl.edu.agh.io.sylvia.dao.ArticleDAO;
import pl.edu.agh.io.sylvia.dao.GroupDAO;
import pl.edu.agh.io.sylvia.dao.MonitorDAO;
import pl.edu.agh.io.sylvia.entity.Article;
import pl.edu.agh.io.sylvia.entity.Group;
import pl.edu.agh.io.sylvia.entity.Keyword;
import pl.edu.agh.io.sylvia.entity.Monitor;
import pl.edu.agh.io.sylvia.entity.Server;

class NewsDaemon implements Runnable {
	
	Logger log = Logger.getAnonymousLogger();

	GroupDAO groupDAO = new GroupDAO();
	MonitorDAO monitorDAO = new MonitorDAO();

	private static Map<String, String> readHeader(Reader reader) throws Throwable {
		HashMap<String, String> map = new HashMap<String, String>();
		BufferedReader bufferedReader = new BufferedReader(reader);
		String line;
		while ((line = bufferedReader.readLine()) != null) {
			Pattern pattern = Pattern.compile("(.*): (.*)");
			Matcher matcher = pattern.matcher(line);
			if (matcher.matches()) {
				System.out.println(matcher.group(1) + ": " + matcher.group(2));
				map.put(matcher.group(1), matcher.group(2));
			}
		}
		return map;
	}

	private static String readBody(Reader reader) throws Throwable {
		StringBuilder stringBuilder = new StringBuilder();
		BufferedReader bufferedReader = new BufferedReader(reader);
		String line;
		while ((line = bufferedReader.readLine()) != null) {
			stringBuilder.append(line);
			stringBuilder.append("\n");
		}
		return stringBuilder.toString();
	}

	private static String asString(List<Keyword> list) {
		if (list.isEmpty()) {
			return "";
		} else if (1 == list.size()) {
			return list.get(0).getWord();
		} else {
			StringBuilder stringBuilder = new StringBuilder();
			for (Keyword keyword : list) {
				stringBuilder.append(keyword.getWord());
				stringBuilder.append(" OR ");
			}
			return stringBuilder.substring(0, stringBuilder.length() - 5);
		}
	}

	private static Article findArticle(Set<Article> set, Article art) {
		for (Article article : set) {
			if (article.equals(art)) return article;
		}
		return null;
	}

	@Override
	public void run() {
		List<Group> groups = groupDAO.getAll();
		List<Monitor> monitors = monitorDAO.getAll();

		Map<Group, IndexWriter> groupMap = new HashMap<Group, IndexWriter>();

		Directory directory = null;
		Analyzer analyzer = null;
		int lastArticle = 0;

		for (Group group : groups) {
			Server server = group.getServer();
			String hostname = server.getHost();
			String username = server.getLogin();
			String password = server.getPassword();
			String groupName = group.getName();

			int firstArticle = (int) group.getLastIndex();
			int port = server.getPort();

			NNTPClient client = null;


			try {
				client = new NNTPClient();
				client.connect(hostname);
				
				if (port != 0) client.setDefaultPort(port);
				if ((username != null) && (password != null)) {
					client.authenticate(username, password);
				}
				
				NewsgroupInfo info = new NewsgroupInfo();
				client.selectNewsgroup(groupName, info);

				analyzer = new StandardAnalyzer();
				directory = new RAMDirectory();
				IndexWriter iWriter = new IndexWriter(directory, analyzer, true);

				if (info.getArticleCount() > 0) {
					lastArticle = info.getLastArticle();
					//int firstArticle2 = info.getFirstArticle();
					//if (firstArticle < firstArticle2) firstArticle = firstArticle2;
					client.selectArticle(firstArticle);
					while(client.selectNextArticle()) {
						Reader reader = client.retrieveArticleHeader();
						Map<String, String> map = readHeader(reader);
						reader = client.retrieveArticleHeader();
						String header = readBody(reader);
						reader = client.retrieveArticleBody();
						String body = readBody(reader);
						Document doc = new Document();
						doc.add(new Field("id", map.get("Message-ID"), Field.Store.YES, Field.Index.NO));
						doc.add(new Field("body", body, Field.Store.YES, Field.Index.TOKENIZED));
						doc.add(new Field("header", header, Field.Store.YES, Field.Index.NO));
						iWriter.addDocument(doc);
					}
				}

				iWriter.optimize();
				iWriter.close();

				groupMap.put(group, iWriter);

			} catch (Throwable e) {
				log.log(Level.SEVERE, e.getMessage(), e);
			}
		}

		Set<Article> articleSet = new HashSet<Article>();

		for (Monitor monitor : monitors) {
			groups = monitor.getGroups();

			for (Group group : groups) {
				if(groupMap.containsKey(group)) {
					List<Keyword> keywords = monitor.getKeywords();

					try {
						IndexSearcher iSearcher = new IndexSearcher(directory);
						QueryParser parser = new QueryParser("body", analyzer);
						Query query = parser.parse(asString(keywords));
						Hits hits = iSearcher.search(query);

						for (int i = 0; i < hits.length(); ++i) {
							System.out.println(hits.score(i));
							String body = hits.doc(i).getField("body").stringValue();
							String header = hits.doc(i).getField("header").stringValue();
							Article tempArticle = new Article(null, header, body, group, new ArrayList<Monitor>());
							if (articleSet.contains(tempArticle)) {
								findArticle(articleSet, tempArticle).getMonitors().add(monitor);
							} else {
								tempArticle.getMonitors().add(monitor);
								articleSet.add(tempArticle);
							}
						}
						
						group.setLastIndex(lastArticle);
						groupDAO.update(group);

					} catch (CorruptIndexException e) {
						log.log(Level.SEVERE, e.getMessage(), e);
					} catch (IOException e) {
						log.log(Level.SEVERE, e.getMessage(), e);
					} catch (ParseException e) {
						log.log(Level.SEVERE, e.getMessage(), e);
					}
				}
			}
		}
		
		// Inserting articles into a database
		ArticleDAO articleDAO = new ArticleDAO();
		for (Article article : articleSet) {
			articleDAO.makePersistent(article);
			System.out.println("[DEBUG] Inserted into DB:");
			System.out.println("[DEBUG] Header:");
			System.out.println(article.getHeader());
			System.out.println("[DEBUG] Body:");
			System.out.println(article.getBody());
		}
	}

}